package com.chuhe.lottery.service.impl;

import com.chuhe.lottery.config.LockService;
import com.chuhe.lottery.dao.PrizePoolsDao;
import com.chuhe.lottery.dao.WinnersDao;
import com.chuhe.lottery.domain.constans.LotteryConstans;
import com.chuhe.lottery.domain.entity.Winners;
import com.chuhe.lottery.domain.enums.AwardsStatusEnum;
import com.chuhe.lottery.mapper.PrizePoolsMapper;
import com.chuhe.lottery.service.LotteryService;
import com.chuhe.lottery.service.adapter.WinnersAdaper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @Author: 楚河
 * @Date: 2025年06月03日17:03
 */
@Service
@Slf4j
public class LotteryServiceImpl implements LotteryService {
    @Autowired
    private PrizePoolsDao prizePoolsDao;
    @Autowired
    private WinnersDao winnersDao;
    @Autowired
    private LockService lockService;
    @Autowired
    private RedisTemplate redisTemplate;

    //配置
    private final ConcurrentHashMap<Integer, Boolean> USER_MAP = new ConcurrentHashMap<>();

    /**
     * 抽奖
     * @Author: 楚河
     * @Date: 2025-06-03 17:10
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public HashMap<String, Object> lottery(int userId) {
        //组装幂等号
        String idempotent = getIdempotent(userId);
        //分布式锁
        return lockService.executeWithLock(idempotent, () -> {
            return lotteryWithTransaction(userId);
        });
    }

    /**
     * 事务处理
     * @Author: 楚河
     * @Date: 2025-06-03 19:09
     */
    @Transactional
    public HashMap<String, Object> lotteryWithTransaction(int userId) {
        HashMap<String, Object> resultMap = new HashMap<>();
        //1. 检查当前用户是否已经抽过奖
        Boolean hasWon = redisTemplate.opsForValue().setIfAbsent("userId_:" + userId, "1", 1, TimeUnit.MINUTES);
        // 键已存在，用户已经抽过奖
        if (!Boolean.TRUE.equals(hasWon)) {
            //临时错误返回
            resultMap.put("result", false);
            log.error("Redis键异常");
            return resultMap;
        }

        //2. 查看数据库是否已经存在该用户
        Winners winners = winnersDao.selectUserById(userId);
        if(Objects.nonNull(winners)){
            //临时错误返回
            resultMap.put("result",false);
            log.error("查询用户数据库异常");
            return resultMap;
        }

        //3. 查询奖品库存
        Integer prizePoolsCount = prizePoolsDao.selectPrizePoolsCount();
        if(prizePoolsCount.equals(LotteryConstans.ZERO)){
            //临时错误返回
            resultMap.put("result",null);
            log.error("查询库存异常");
            return resultMap;
        }

        //4. TODO 抽奖...
        int winnerNum = toLottery();

        if(winnerNum < AwardsStatusEnum.FIRST_PRIZE.getPid() || winnerNum == AwardsStatusEnum.ERROR_PRIZE.getPid()){
            //临时错误返回
            resultMap.put("result",false);
            return resultMap;
        }

        //5. 库存扣减（乐观锁）
        prizePoolsDao.delPrizePoolsCount(winnerNum);

        //6. 记录入库（单条新增操作本身具有原子性，不需要在此处再做其它处理）
        Winners winnersUserInfo = WinnersAdaper.buildWinnerUserInfo(userId,Thread.currentThread().getName(),winnerNum);
        winnersDao.save(winnersUserInfo);

        //7. 返回
        resultMap.put("result",true);
        return resultMap;
    }

    /**
     * 组装幂等号
     * @Author: 楚河
     * @Date: 2025-06-03 19:04
     */
    private String getIdempotent(int userId) {
//        int segments = 16;
//        Integer value = Integer.valueOf(userId);
//        int segmentId = Math.abs(value.hashCode() % segments);
//        String lockKey = "lottery_segment:" + segmentId;
        return String.format("lock_Key:%d",userId);
    }

    /**
     * 抽奖逻辑 (简易测试版)
     * @Author: 楚河
     * @Date: 2025-06-03 18:29
     */
    private int toLottery() {
        int prizeNum;
        //生成随机数吧
        int randomNum = ThreadLocalRandom.current().nextInt(LotteryConstans.INT_100);
        //简单中奖测试
        if(randomNum < LotteryConstans.INT_5){
            prizeNum = AwardsStatusEnum.FIRST_PRIZE.getPid();
        } else if(randomNum < LotteryConstans.INT_15){
            prizeNum = AwardsStatusEnum.SECOND_PRIZE.getPid();
        } else if(randomNum < LotteryConstans.INT_40){
            prizeNum = AwardsStatusEnum.THIRD_PRIZE.getPid();
        } else {
            prizeNum = AwardsStatusEnum.ERROR_PRIZE.getPid();
        }
        return prizeNum;
    }
}
